/**
 * Adsend using the comedi libary.
 *
 * Matthew W. Bone
 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include "adsend_comedi.h"
#include "daq_functions.h"
#include "getconfig.h"
#include <earthworm.h>
#include <transport.h>
#include <trace_buf.h>
#include <string.h>
#include <pthread.h>

SHM_INFO outRegion;
pid_t myPid;

pthread_mutex_t trace_mutex;
pthread_cond_t trace_cond;
pthread_t packager_thread, results_thread;

int main(int argc, char **argv) {
    //Get the command line arguments
    if(argc<2) {
	usage();
	return -1;
    }
	
    pthread_mutex_init(&trace_mutex, NULL);
    pthread_cond_init (&trace_cond, NULL);

    ew_adsend_config_file *configfile;

    //Initialize name of logfile and open it.
    logit_init(argv[1], 0, 256, 1);
    logit("ew","adsend_comedi: started\n");

    configfile = GetConfig(argv[1]);
    LogConfig(configfile);
    logit("ew", "adsend_comedi: configfile processed\n");

    //initialize buffers and variables
    tport_attach(&outRegion, 1000); //find what this key should be
    logit("ew","adsend_comedi: attached to shared memory segment\n");

	
    //initialize daq card
    daq_handle *handle = initialize_daq(configfile);
    start_acquisition(handle);

    //initialize the trace queue:
    handle->trace_queue = malloc(sizeof(TraceDataQueue));
    handle->trace_queue->first = 0;
    handle->trace_queue->last  = 0;
    handle->trace_queue->count = 0;

    int i =0;
    for(i=0;i<TD_LEN;i++) {
	handle->trace_queue->queue[i] = malloc(sizeof(TRACE_HEADER)+ 
					      ADSEND_COMEDI_MAX_SAMPLES*sizeof(long));
    }

    pthread_create(&results_thread, NULL, 
		   (void *) get_results_thread, (void *) handle);
    
    pthread_create(&packager_thread, NULL, 
    	   (void *) earthworm_packager_thread, (void *) handle);
   
    //threads don't complete until earthworm tells us we need to shutdown:
    pthread_join(results_thread, NULL);
    pthread_join(packager_thread, NULL);

    stop_acquisition(handle);
  	
    logit("ew", "adsend_comedi: shutting down, goodbye!\n");
    return 0;  
}

void get_results_thread(daq_handle *handle) {

    /**
     * Main loop of the program where we continually acquire data and 
     * send it off to the transport ring.
     */
    while(tport_getflag(&outRegion)!=TERMINATE) {
	get_results(handle, handle->trace_queue);
    }

    logit("ew", "adsend_comedi: stopping threads...\n");

    //signal the other thread to stop (actually we're just walking it up 
    //so that it can check and see that it should stop)
    pthread_mutex_lock(&trace_mutex);
    pthread_cond_signal(&trace_cond);
    pthread_mutex_unlock(&trace_mutex);
    logit("ew", "adsend_comedi: get results thread complete.\n");
    
}
  
void earthworm_packager_thread(daq_handle *handle) {

    MSG_LOGO msg_logo = handle->configfile->msg_logo;

    while(tport_getflag(&outRegion)!=TERMINATE) {
	TRACE_HEADER *header = pop_trace_data(handle->trace_queue);//blocking

	if(header == NULL) {
	    break;
	}
	int totalsize = sizeof(TRACE_HEADER) + header->nsamp*sizeof(long);
	
	int rc = tport_putmsg(&outRegion, &msg_logo, totalsize, (char *) header);
		
	if (rc == PUT_TOOBIG) {
	    printf("Trace message too big\n");
	} else if (rc == PUT_NOTRACK) {
	    printf("Tracking error.\n");
	    }
    }
    logit("ew", "adsend_comedi: packager thread complete.\n");
}

/**
 * 
 * Circular queue with no bounds checking.
 */
TRACE_HEADER *get_next_blank_trace_header(TraceDataQueue *trace_queue) {
    pthread_mutex_lock(&trace_mutex);
    TRACE_HEADER *return_val = trace_queue->queue[trace_queue->last];
    memset(return_val,0, sizeof(TRACE_HEADER)+ ADSEND_COMEDI_MAX_SAMPLES*sizeof(long));
    trace_queue->last = (trace_queue->last+1) % TD_LEN;
    //    printf("queue first, last: %d %d\n", trace_queue->first, trace_queue->last);
    pthread_mutex_unlock(&trace_mutex);
    
    return return_val;
}

void next_trace_header_ready(TraceDataQueue *trace_queue) {
    pthread_mutex_lock(&trace_mutex);
    trace_queue->count++;
    if(trace_queue->count-1==0) {
	pthread_cond_signal(&trace_cond);
    }
    //    printf("next header ready, count: %d\n", trace_queue->count);
    pthread_mutex_unlock(&trace_mutex);
}

/**
 * Circular queue with no bounds checking.
 */
TRACE_HEADER *pop_trace_data(TraceDataQueue *trace_queue) {
    pthread_mutex_lock(&trace_mutex);
    if(trace_queue->count==0) {
	pthread_cond_wait(&trace_cond, &trace_mutex);
    }
    TRACE_HEADER *return_header = trace_queue->queue[trace_queue->first];
    trace_queue->first = (trace_queue->first+1)%TD_LEN;
    trace_queue->count--;
    pthread_mutex_unlock(&trace_mutex);

    return return_header;
}

void usage() {
    printf( "Usage: adsend_comedi <config file>\n" );
}
